<?php


namespace App\Http\Controllers;

use App\Models\Activity;
use App\Models\ActivityApply;
use App\Models\ActivitySign;
use App\Models\Reservation;
use App\Models\ReservationApply;
use App\Models\ReservationSchedule;
use App\Models\ReservationSeat;
use App\Models\StudyRoomReservation;
use App\Models\StudyRoomReservationApply;
use App\Models\StudyRoomReservationRemoteOperation;
use App\Models\StudyRoomReservationSchedule;
use App\Models\UserInfo;
use App\Models\UserViolate;
use App\Validate\ScanQrValidate;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * 扫描二维码
 * Class SanQr
 * @package app\port\controller
 */
class ScanQrController extends Controller
{
    protected $validate = null;
    protected $user_id = null;
    protected $account_id = null;

    public function __construct()
    {
        parent::__construct();
        $this->validate = new ScanQrValidate();
    }

    /**
     * 扫描二维码
     * @param  qr_code  二维码code  固定前 2位 活动为 11   预约为 12  座位预约（座位号）为 13   23、空间预约
     * @param  lon 经度
     * @param  lat 纬度
     * @param  way 签到方式  1 签到，2签退 (第一次扫码默认传0) （只有当用户已签过到，再次签到才有效）
     */
    public function scanAffirm()
    {
        //__construct 是在中间件前执行的，所以需要放在这里执行
        $user_id = request()->user_info['id'];
        $account_id = request()->user_info['account_id'];

        //增加验证场景进行验证
        if (!$this->validate->scene('scan_qr')->check(request()->all())) {
            return $this->returnApi(201,  $this->validate->getError());
        }

        Log::error('签到:' . json_encode(request()->all()));

        $qr_code = $this->request->qr_code;
        $prefix = substr($qr_code, 0, 4);

        if ($prefix != 'CDBT') {
            $prefix = substr($qr_code, 0, 2);
        }
        $lon = $this->request->lon;
        $lat = $this->request->lat;
        $way = $this->request->way;

        if (empty($lon) || empty($lat)) {
            return $this->returnApi(202, "请先开启手机定位功能");
        }

        //其他单独判断
        switch ($prefix) {
            case 11:
                return $this->scanActSign($qr_code, $user_id, $account_id);
                break;
            case 12:
                return $this->scanReservationSign($qr_code, $user_id, $lon, $lat); //除座位预约外的其他预约
                break;
            case 13:
                return $this->scanSeatSign($qr_code, $user_id, $lon, $lat, $way); //座位预约
                break;
            case 23:
                return $this->scanStudyRoomSign($qr_code, $user_id, $lon, $lat, $way); //空间预约
                break;
            case 'CDBT':
                return $this->disSanQrCdbt($qr_code, $user_id, $lon, $lat, $way); //扫码CDBT统一处理
                break;
            default:
                return $this->returnApi(202, "请扫描正确二维码");
        }
    }


    /**
     * 用户扫码签到 活动
     * @param $authorrization 用户 token    例：Bearer 1 必选
     * @param qr_code 活动code  固定前 2位 为 11
     */
    public function scanActSign($qr_code, $user_id, $account_id)
    {
        $activityApplyModel = new ActivityApply();
        $activityModel = new Activity();
        //处理逾期未签到的数据
        $activityApplyModel->checkApplyStatus();

        // 活动是否存在
        $act_info = $activityModel->select([
            'id', 'title', 'is_apply', 'apply_number', 'is_long', 'start_time', 'end_time', 'everyday_start_time', 'is_continue', 'is_qr',
            'everyday_end_time', 'is_reader', 'sign_way', 'apply_start_time', 'apply_end_time', 'number'
        ])
            ->where("sign_qr_code", $qr_code)
            ->where("is_del", 1)
            ->where("is_play", 1)
            ->first();
        if (empty($act_info)) return $this->returnApi(202, "活动不存在或已被删除");
        if ($act_info['end_time'] < date("Y-m-d H:i:s")) return $this->returnApi(202, '活动已经结束,不能签到');
        if ($act_info['is_qr'] == 2) {
            return $this->returnApi(202, "此活动无需用户扫码签到！"); //不支持签到
        }
        if ($act_info['sign_way'] == 3) {
            return $this->returnApi(202, "此活动不支持用户扫码签到！"); //不支持签到
        }

        // 查询用户报名信息
        $res = $activityApplyModel->select('id', 'status', 'sign_num')
            ->where('user_id', $user_id)
            ->where('act_id', $act_info['id'])
            ->where('status', [1, 4])
            ->orderByDesc('id')
            ->first();

        if ($act_info['is_apply'] == 1) {
            //报名需要审核的活动，需要先报名成功
            if (empty($res)) {
                return $this->returnApi(202, '暂无报名信息，请先报名');
            } elseif ($res['status'] == 4) {
                return $this->returnApi(202, '您的报名，正在审核中，暂不能签到');
            }
        } else {
            //只有当 无需报名，并且需要签到（二维码进场）才能自动报名（此时也不能设置报名填写的信息），否则不能自动报名
            if ($act_info['is_qr'] == 1 && !empty($act_info['number'])) {
                //只判断名额，其余不判断
                if ($act_info['apply_number'] >= $act_info['number']) {
                    return $this->returnApi(202, '名额已满，不允许报名签到');
                }
            }
        }

        /**检查签到时间 */
        $date = time();
        //长期活动，在每日的开始前60分钟 ~ 结束的前0分钟为签到时间；短期活动，在活动的开始前60分钟 ~ 结束的前0分钟为签到时间；
        if ($act_info['is_long'] == 1) {
            if ($date < strtotime(date("Y-m-d") . ' ' . $act_info['everyday_start_time']) - 60 * 60 || $date >= strtotime(date("Y-m-d") . ' ' . $act_info['everyday_end_time'])) {
                return $this->returnApi(202, '当前时间不在打卡范围内');
            }
        } else {
            if ($date < strtotime($act_info['start_time']) - 60 * 60 || $date >= strtotime($act_info['end_time'])) {
                return $this->returnApi(202, '当前时间不在打卡范围内');
            }
        }

        //签到
        DB::beginTransaction();
        try {
            /**未查询到用户报名信息自动为用户报名 */
            if (empty($res) && $act_info['is_qr'] == 1 && $act_info['is_apply'] == 2) {

                /*自动报名-检查是否违规*/
                $validateModel = new UserViolate();
                $isViolate = $validateModel->checkIsViolate($user_id, 'activity');
                if ($isViolate) {
                    throw new Exception('已达到违规次数上限,不能参加活动');
                }

                $user = UserInfo::find($user_id);

                $applyData = [
                    'user_id' => $user_id,
                    'act_id' => $act_info['id'],
                    'account_id' => $user['account_id'] ? $user['account_id'] : 0,
                    'status' => 1,
                ];

                $insert_res = $activityApplyModel->add($applyData);

                if ($insert_res === false) {
                    throw new Exception('签到失败,请稍后再试');
                }

                $res = $activityApplyModel->select('id', 'sign_num')
                    ->where('user_id', $user_id)
                    ->where('act_id', $act_info['id'])
                    ->where('status', 1)
                    ->orderByDesc('id')
                    ->first();

                //增加报名人数
                $act_info->apply_number = ++$act_info->apply_number;
                $act_info->save();
            }

            //不是长期活动，只能签到一次
            if ($act_info->is_long == 2 && $res->sign_num > 0) {
                throw new Exception("您已签到，请勿重复签到");
            }

            //判断今日是否签到
            $activitySignModel = new ActivitySign();
            $day_sign = $activitySignModel
                ->where('act_id', $act_info['id'])
                ->where('apply_id', $res['id'])
                ->where('user_id', $user_id)
                ->where('date', date('Y-m-d'))
                ->first();
            if (!empty($day_sign)) {
                throw new Exception('今日已签到，请勿重复签到');
            }


            $res->sign_num = $res['sign_num'] + 1;
            $res->save();

            # 录入签到列表
            $activitySignData = [
                "act_id" => $act_info['id'],
                "apply_id" => $res['id'],
                "user_id" => $user_id,
                "date" => date("Y-m-d"),
                "yyy" => date("Y"),
                "mmm" => date("m"),
                "ddd" => date("d"),
            ];
            $activitySignModel->add($activitySignData);

            //用户积分操作  与 系统消息
            $system_id = $this->systemAdd('活动签到成功', $user_id, $account_id, 14, $act_info['id'], '【' . $act_info['title'] . '】活动签到成功'); //添加系统消息

            /**积分 //如果已绑定读者证*/
            if (config('other.is_need_score') && $res->sign_num <= 1 && $act_info->is_reader == 1) {
                //增加签到积分
                if ($act_info['is_long'] == 2) {
                    if ($date < strtotime($act_info['start_time'])) {
                        $score_type = 20; //正常签到
                    } else {
                        $score_type = 21; //迟到签到
                    }
                    //操作积分
                    $scoreRuleObj = new ScoreRuleController();
                    $score_status = $scoreRuleObj->checkScoreStatus($score_type, $user_id, $account_id);
                    if ($score_status['code'] == 202 || $score_status['code'] == 203) {
                        throw new Exception($score_status['msg']);
                    }
                    if ($score_status['code'] == 200) {
                        $scoreRuleObj->scoreChange($score_status, $user_id, $account_id, $system_id); //添加积分消息
                    }
                }
            }
            // 提交事务
            DB::commit();
            return $this->returnApi(
                200,
                '活动:【' . $act_info['title'] . '】签到成功！',
                true
            );
        } catch (\Exception $e) {
            Log::error('签到错误:' . $e->getMessage() . ';错误文件：' . $e->getFile() . ';错误行数：' . $e->getLine());
            // 回滚事务
            DB::rollBack();
            //  return $this->returnApi(202, !empty($e->getMessage()) ? $e->getMessage() . $e->getFile() . $e->getLine() : '签到失败');
            return $this->returnApi(202, !empty($e->getMessage()) ? $e->getMessage() : '签到失败');
        }
    }



    /**
     * 用户扫码签到 预约 到馆（如果已预约其他，也可直接进入）
     */
    public function scanReservationSign($qr_code, $user_id, $lon, $lat)
    {
        $reservationModel = new Reservation();
        // 自动验证
        $reservation_info = $reservationModel->where('qr_code', $qr_code)->where('is_play', 1)->where('is_del', 1)->first();
        if (!$reservation_info) {
            return $this->returnApi(201, "此预约不存在或已被删除");
        }
        if ($reservation_info['sign_way'] == 3) {
            return $this->returnApi(202, "此预约不支持用户扫码签到！"); //不支持签到
        }
        //验证距离
        $scan_sign_valid_range = config('other.scan_sign_valid_range');
        $distance = get_distance($reservation_info['lon'], $reservation_info['lat'], $lon, $lat);
        if ($distance > $scan_sign_valid_range) {
            Log::error("ID：" . $qr_code);
            Log::error("经纬度：" . $lon . '~' . $lat);
            Log::error('设置范围：' . $scan_sign_valid_range);
            Log::error("当前距离：" . $distance);
            return $this->returnApi(201, '当前位置不在有效签到范围内');
        }

        $reservation_id = $reservation_info->id;

        if ($reservation_info['is_sign'] == 2) {
            return $this->returnApi(202, "此预约无须签到");
        }

        $week = date("w");
        $weeks = $week ? $week : 7;

        //查看指定日期是否有特殊时间设置
        if ($reservation_info['node'] != 7) {
            $reservationScheduleModel = new ReservationSchedule();
            list($schedule_type, $reservation_schedule) = $reservationScheduleModel->getReservationSchedule($reservation_id, $weeks);
            if ($schedule_type === false) {
                return $this->returnApi(202, $reservation_schedule);
            }
        }

        $reservationApplyModel = new ReservationApply();
        $apply = $reservationApplyModel->nowTimeMakeInfo($user_id, $reservation_id, $reservation_schedule['id'], $schedule_type, null, [1, 6], date('Y-m-d'), $reservation_info['node']);


        //如果是到馆预约的二维码，并且未预约到馆，可以查看其他 是否有预约
        if ($reservation_schedule && empty($apply) && $reservation_info->node == 5) {
            //查询这个时间段是否有其他预约(除到馆预约外的预约)
            $is_exists = $reservationModel->isExistsOtherMake($user_id, $weeks, [1, 2, 3, 4, 6, 7]);
            if ($is_exists) {
                if ($is_exists['status'] == 1 && date('Y-m-d H:i:s') > $is_exists['expire_time']) {
                    return $this->returnApi(201, "当前时间段，未查询到任何预约信息"); // "已超过开始打卡时间，不能打卡"
                }

                return $this->returnApi(
                    200,
                    "查询成功,已预约“" . $reservationModel->getNodeName($is_exists['node']) . "”，时间段为:【" . date('Y-m-d') . ' ' . $is_exists['start_time'] . '~' . $is_exists['end_time'] . "】",
                    true
                ); //直接签到成功
            } else {
                //只是没查询到
                return $this->returnApi(201, "当前时间段，未查询到任何预约信息");
            }
        } elseif (empty($apply)) {
            //只是没查询到
            return $this->returnApi(201, "当前时间段，未查询到任何预约信息");
        }

        //判断是否过了打卡时间
        if ($apply->status == 1 && date('Y-m-d H:i:s') > $apply->expire_time) {
            return $this->returnApi(201, "您的预约，已过签到时间，请联系管理员处理！");
        }

        if ($apply->status == 6) {
            return $this->returnApi(201, "您已签到，无需重复签到！");
        }

        // 启动事务
        DB::beginTransaction();
        try {
            $apply->status = 6;
            $apply->sign_time = date('Y-m-d H:i:s');
            $apply->save();

            $node_name = $reservationModel->getNodeName($reservation_info['node']);

            //用户积分操作  与 系统消息
            $this->systemAdd($node_name . '预约签到成功', $user_id, request()->user_info['account_id'], 48, $apply->id, '【' . $reservation_info->name . '】签到成功'); //添加系统消息

            // 提交事务
            DB::commit();
            return $this->returnApi(
                200,
                $node_name . ":【" . $reservation_info->name . "】签到成功；预约时间段为:【" . date('Y-m-d') . ' ' . $reservation_schedule['start_time'] . '~' . $reservation_schedule['end_time'] . "】",
                true
            ); //直接签到成功
        } catch (\Exception $e) {
            Log::error('签到错误:' . $e->getMessage() . ';错误文件：' . $e->getFile() . ';错误行数：' . $e->getLine());
            // 回滚事务
            DB::rollBack();
            return $this->returnApi(202, "签到失败");
        }
    }


    /**
     * 座位预约打卡签到
     * @param code string 二维码code
     */
    public function scanSeatSign($qr_code, $user_id, $lon, $lat, $way)
    {
        $reservationApplyModel = new ReservationApply();
        $reservationSeatModel = new ReservationSeat();
        $reservationModel = new Reservation();

        $reservation_seat_info = $reservationSeatModel->where('qr_code', $qr_code)->where('is_del', 1)->where('is_play', 1)->first();
        if (!$reservation_seat_info) {
            return $this->returnApi(201, "此座位不存在或已被删除");
        }
        $reservation_id = $reservation_seat_info->reservation_id;
        // 自动验证
        $reservation_info = $reservationModel->detail($reservation_id, ['id', 'sign_way', 'name', 'node', 'lon', 'lat']);
        if (!$reservation_info) {
            return $this->returnApi(201, "此预约不存在或已被删除");
        }
        if ($reservation_info['sign_way'] == 3) {
            return $this->returnApi(202, "此预约不支持用户扫码签到！"); //不支持签到
        }
        //验证距离
        $scan_sign_valid_range = config('other.scan_sign_valid_range');
        $distance = get_distance($reservation_info['lon'], $reservation_info['lat'], $lon, $lat);
        if ($distance > $scan_sign_valid_range) {
            Log::error("ID：" . $qr_code);
            Log::error("经纬度：" . $lon . '~' . $lat);
            Log::error('设置范围：' . $scan_sign_valid_range);
            Log::error("当前距离：" . $distance);
            return $this->returnApi(201, '当前位置不在有效签到范围内');
        }

        $week = date("w");
        $weeks = $week ? $week : 7;

        //查看指定日期是否有特殊时间设置
        $reservationScheduleModel = new ReservationSchedule();
        list($schedule_type, $reservation_schedule) = $reservationScheduleModel->getReservationSchedule($reservation_id, $weeks);
        if ($schedule_type === false) {
            return $this->returnApi(202, $reservation_schedule);
        }

        /**查询该时段时候有申请记录 */
        $apply = $reservationApplyModel->nowTimeMakeInfo($user_id, $reservation_id, $reservation_schedule['id'], $schedule_type, $reservation_seat_info->id, [1, 6], date('Y-m-d'), $reservation_info['node']);

        if (empty($apply)) {
            return $this->returnApi(202, "当前时间段，未查询到任何预约信息");
        }

        if ($apply->status == 1 && date('Y-m-d H:i:s') > strtotime($apply->expire_time)) {
            return $this->returnApi(201, "您的预约，已过期，不能进行打卡；");
        }
        $msg = '';
        if ($apply->status == 1) {
            $apply->status = 6;
            $apply->sign_time = date("Y-m-d H:i:s", time());
            $msg = "签到成功";
        } else if ($apply->status == 6) {
            if ($apply->sign_time > date('Y-m-d H:i:s', strtotime("-3 min"))) {
                return $this->returnApi(202, "您已签到，请勿重复签到");
            }
            if ($way != 2) {
                return $this->returnApi(210, "您确定要签退吗？"); //需用户确认签退
            }

            $apply->status = 7; //已签退
            $apply->sign_end_time = date("Y-m-d H:i:s", time());
            $msg = "签退成功";
        } else if ($apply->status == 7) {
            return $this->returnApi(201, "已成功结束打卡,请勿重复打卡");
        }
        DB::beginTransaction();
        try {
            $apply->save();
            //用户积分操作  与 系统消息
            $this->systemAdd('预约' . $msg, $user_id, $apply->account_id, 48, $reservation_id, '【' . $reservation_info['name'] . '】' . $msg); //添加系统消息

            DB::commit();
            return $this->returnApi(
                200,
                "座位预约:【" . $reservation_info['name'] . "】" . $msg . ";预约时间段为:【" . date('Y-m-d') . ' ' . $reservation_schedule['start_time'] . '~' . $reservation_schedule['end_time'] . "】",
                true
            );
        } catch (\Exception $e) {
            Log::error('签到错误:' . $e->getMessage() . ';错误文件：' . $e->getFile() . ';错误行数：' . $e->getLine());
            DB::rollBack();
            return $this->returnApi(202, "打卡失败,请稍后再试");
        }
    }

    /**
     * 打卡签到、签退
     * @param authorrization ：用户 token   必选
     * @param reservation_id int 预约id
     */
    public function scanStudyRoomSign($qr_code, $user_id, $lon, $lat, $way)
    {
        $reservationApplyModel = new StudyRoomReservationApply();
        $reservationModel = new StudyRoomReservation();

        $reservation_info = $reservationModel->where('qr_code', $qr_code)->where('is_del', 1)->where('is_play', 1)->first();
        if (!$reservation_info) {
            return $this->returnApi(201, "此预约不存在或已被删除");
        }
        $reservation_id = $reservation_info->id;
        //验证距离
        $scan_sign_valid_range = config('other.scan_sign_valid_range');
        $distance = get_distance($reservation_info['lon'], $reservation_info['lat'], $lon, $lat);
        if ($distance > $scan_sign_valid_range) {
            Log::error("ID：" . $qr_code);
            Log::error("经纬度：" . $lon . '~' . $lat);
            Log::error('设置范围：' . $scan_sign_valid_range);
            Log::error("当前距离：" . $distance);
            return $this->returnApi(201, '当前位置不在有效签到范围内');
        }

        $week = date("w");
        $weeks = $week ? $week : 7;

        //查看指定日期是否有特殊时间设置
        $reservationScheduleModel = new StudyRoomReservationSchedule();
        list($schedule_type, $reservation_schedule) = $reservationScheduleModel->getReservationSchedule($reservation_id, $weeks);
        if ($schedule_type === false) {
            return $this->returnApi(202, $reservation_schedule);
        }

        /**查询该时段时候有申请记录 */
        $apply = $reservationApplyModel->nowTimeMakeInfo($user_id, $reservation_id, $reservation_schedule['id'], $schedule_type, [1, 6], date('Y-m-d')); //配置 1，6 ，只允许签到的，所以只配置 6

        if (empty($apply)) {
            return $this->returnApi(202, "当前时间段，未查询到任何预约信息");
        }

        if ($apply->status == 1 && date('Y-m-d H:i:s') > strtotime($apply->expire_time)) {
            return $this->returnApi(201, "您的预约，已过期，不能进行打卡；");
        }
        $msg = '';
        if ($apply->status == 1) {
            if ($reservation_info['sign_way'] == 3) {
                return $this->returnApi(202, "请在门禁机上进行签到！"); //不支持签到
            }
            $apply->status = 6;
            $apply->sign_time = date("Y-m-d H:i:s", time());
            $msg = "签到成功";
        } else if ($apply->status == 6) {
            //第二次签到，只进行开门操作
            if ($way == 1) {
                DB::beginTransaction();
                try {
                    //只进行开门操作
                    $studyRoomReservationRemoteOperationModel = new StudyRoomReservationRemoteOperation();
                    $studyRoomReservationRemoteOperationModel->add($reservation_id, 1, $user_id, 1);
                    DB::commit();
                    return $this->returnApi(202, "操作成功");
                } catch (\Exception $e) {
                    DB::rollBack();
                    return $this->returnApi(202, "操作失败,请稍后再试");
                }
            }

            if ($way != 2) {
                return $this->returnApi(211, "请选择需要的操作？"); //需用户确认签退,选择签到还是签退
            }

            if ($apply->sign_time > date('Y-m-d H:i:s', strtotime("-3 min"))) {
                return $this->returnApi(202, "您刚签到，请3分钟后再来签退！");
            }

            $apply->status = 7; //已签退
            $apply->sign_end_time = date("Y-m-d H:i:s", time());
            $msg = "签退成功";
        } else if ($apply->status == 7) {
            return $this->returnApi(201, "已成功结束打卡,请勿重复打卡");
        }
        DB::beginTransaction();
        try {

            $apply->save();
            //还需要进行开门操作
            //前面已修改了状态，这里是需要status 为 6 才对
            if ($apply->status == 6) {
                $studyRoomReservationRemoteOperationModel = new StudyRoomReservationRemoteOperation();
                $studyRoomReservationRemoteOperationModel->add($reservation_id, 1, $user_id, 1);
            }

            //用户积分操作  与 系统消息
            $this->systemAdd('预约' . $msg, $user_id, $apply->account_id, 51, $reservation_id, '【' . $reservation_info['name'] . '】' . $msg); //添加系统消息

            DB::commit();
            return $this->returnApi(
                200,
                "空间预约:【" . $reservation_info['name'] . "】" . $msg . ";预约时间段为:【" . date('Y-m-d') . ' ' . $reservation_schedule['start_time'] . '~' . $reservation_schedule['end_time'] . "】",
                true
            );
        } catch (\Exception $e) {
            Log::error('签到错误:' . $e->getMessage() . ';错误文件：' . $e->getFile() . ';错误行数：' . $e->getLine());
            DB::rollBack();
            return $this->returnApi(202, "打卡失败,请稍后再试");
        }
    }


    /** 
     * 用户自主扫码操作领取和归还钥匙
     * @param id int 预约id
     * @param apply_id int 申请记录id （与code 2选一）
     * @param type string 若值为：code   则是 二维码签到模式   则 code值必须要有
     * @param code string 二维码值或者读者证号
     * @param seat_id string 领取钥匙编号  领取必填 （领取时，若钥匙编号不存在，则不需要选择,直接传列表的座位号）
     */
    public function disSanQrCdbt($qr_code, $user_id, $lon, $lat, $way)
    {
        $code_data = $this->dataDecrypt($qr_code);
        if (empty($code_data) || empty($code_data['node']) || empty($code_data['qr_code']) || empty($code_data['expire_time'])) {
            return $this->returnApi(202, "无效二维码");
        }
        if ($code_data['expire_time'] < date('Y-m-d H:i:s', strtotime('-5 second'))) {
            return $this->returnApi(202, "二维码已过期");
        }
        if ($code_data['node'] == 'reservation') {
            return $this->collectionAndReturn($code_data, $user_id, $lon, $lat, $way);
        }
    }


    /** 
     * 用户自主扫码操作领取和归还钥匙
     * @param id int 预约id
     * @param code_data int 申请记录id （与code 2选一）
     * @param user_id 用户id
     * @param lon string 
     * @param lat string 
     */
    public function collectionAndReturn($code_data, $user_id, $lon, $lat, $way)
    {
        $reservation_info = Reservation::where('qr_code', $code_data['qr_code'])->where('is_play', 1)->where('is_del', 1)->first();
        if (empty($reservation_info)) {
            return $this->returnApi(201, "当前预约不存在");
        }
        if ($reservation_info['sign_way'] != 2) {
            return $this->returnApi(201, "当前预约不存在");
        }

        //验证距离
        $scan_sign_valid_range = config('other.scan_sign_valid_range');
        $distance = get_distance($reservation_info['lon'], $reservation_info['lat'], $lon, $lat);
        if ($distance > $scan_sign_valid_range) {
            Log::error("ID：" . $code_data['qr_code']);
            Log::error("设置经纬度：" . $reservation_info['lon'] . '~' . $reservation_info['lat']);
            Log::error("用户经纬度：" . $lon . '~' . $lat);
            Log::error('设置范围：' . $scan_sign_valid_range);
            Log::error("当前距离：" . $distance);
            return $this->returnApi(201, '当前位置不在有效签到范围内');
        }

        $reservationApplyInfoModel = new ReservationApply();
        //预约状态   1.已通过  2.已取消，3待审核，4已拒绝 ,5 已过期 (签到过期）, 6 已签到（已领取）  7 结束打卡（增对座位预约是已归还，钥匙领取时已归还）
        $reservation_apply = $reservationApplyInfoModel->where('user_id', $user_id)
            ->where('reservation_id', $reservation_info['id'])
            ->whereIn('status', [1, 3, 5, 6])
            ->orderByDesc('id')
            ->first();
        if (empty($reservation_apply)) {
            return $this->returnApi(202, "预约信息不存在");
        }
        if ($reservation_apply['status'] == 5) {
            return $this->returnApi(201, "领取时间已过期");
        }
        if ($reservation_apply['status'] == 3) {
            return $this->returnApi(201, "预约信息正在审核中，请稍后再试");
        }
        if (empty($way)) {
            if ($reservation_apply['status'] == 1) {
                return $this->returnApi(212, "您确定要领取钥匙吗？");
            }
            if ($reservation_apply['status'] == 6) {
                return $this->returnApi(213, "您确定要归还钥匙吗？");
            }
        }
        $seat_id = $reservation_apply['seat_id'];

        if (($reservation_apply['status'] == 1 && $way == 2) || ($reservation_apply['status'] == 6 && $way == 1)) {
            return $this->returnApi(201, "当前状态无法进行操作");
        }

        if ($reservation_apply->status == 1 && date('Y-m-d H:i:s') > $reservation_apply->expire_time) {
            return $this->returnApi(201, "已超过领取时间，不能领取");
        }

        if ($reservation_apply->status == 1 && (empty($seat_id) || !is_numeric($seat_id))) {
            return $this->returnApi(201, "领取编号ID规则不正确");
        }

        if ($reservation_apply->status == 6) {
            // if ($reservation_info['node'] != 5 && $reservation_info['node'] != 6) {
            //     return $this->returnApi(201, "预约状态有误，不能签到！");
            // }
            //3分钟内不能直接归还
            if ($reservation_apply->sign_time > date('Y-m-d H:i:s', strtotime("-3 min"))) {
                return $this->returnApi(202, "您刚领取，如需归还，请3分钟后重试！");
            }
        }

        // 启动事务
        Db::beginTransaction();
        try {
            if ($reservation_apply->status == 1) {
                $reservation_apply->seat_id = $seat_id;
                $reservation_apply->return_expire_time = date('Y-m-d H:i:s', strtotime(date('Y-m-d H:i:s') . "+" . $reservation_info['return_time'] . " hour"));
                $reservation_apply->status = 6;
                $reservation_apply->sign_time = date("Y-m-d H:i:s");
                $msg = "操作成功,请及时联系管理员,领取钥匙!";
            } else {
                if ($reservation_apply->return_expire_time < date('Y-m-d H:i:s') && $reservation_apply['is_violate'] == 1) {
                    $reservation_apply->is_violate = 2; //自动违规
                    $reservation_apply->violate_reason = '逾期归还'; //自动违规
                    $reservation_apply->violate_time = $reservation_apply->return_expire_time;

                    $controllerObj = new Controller();
                    $controllerObj->systemAdd('预约已违规', $reservation_apply->user_id, $reservation_apply->account_id, 33, $reservation_apply->id, '您申请的预约：【' . $reservation_info['name'] . '】逾期归还，自动违规', $reservation_apply->return_expire_time);
                }

                $reservation_apply->status = 7;
                $reservation_apply->sign_end_time = date('Y-m-d H:i:s');
                $msg = "操作成功,请及时将钥匙归还给管理员!";
            }
            $reservation_apply->save();
            /*消息推送*/
            $reservation_name = $reservation_info['name'];
            $system_id = $this->systemAdd("预约" . $msg . "：", $reservation_apply->user_id,  $reservation_apply->account_id, 48, intval($reservation_apply->id), '【' . $reservation_name . '】预约' . $msg);
            // 提交事务
            DB::commit();
            return $this->returnApi(200, $msg, true);
        } catch (\Exception $e) {
            $msg = $e->getCode() == 202 ? $e->getMessage() : '操作失败';
            // 回滚事务
            DB::rollBack();
            return $this->returnApi(202, $msg);
        }
    }
}
